package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"zinx/utils"
	"zinx/ziface"
)

type Connection struct {
	//当前Conn属于哪个Server
	TcpServer ziface.IServer
	//当前连接的socket TCP 套接字
	Conn *net.TCPConn
	//当前连接的ID，也可以称为SessionID，ID全局唯一
	ConnID uint32
	//当前连接的关闭状态
	isClosed bool

	////该连接的处理方法API
	//Router ziface.IRouter
	//消息管理msgId 和对应处理方法的消息管理模块
	MsgHandle ziface.IMsgHandle
	//告知该连接已经退出/停止 的channel
	ExitBuffChan chan bool
	//无缓冲管道，用于读、写两个Goroutine之间消息通信
	msgChan chan []byte
	//有缓冲管道，用于读、写 两个Goroutine之间的消息通信
	msgBufChan chan []byte

	//连接属性
	property map[string]interface{}
	//保护连接属性修改的锁
	propertyLock sync.RWMutex
}

// NewConnection 创建连接的方法
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandle ziface.IMsgHandle) *Connection {
	c := &Connection{
		TcpServer:    server,
		Conn:         conn,
		ConnID:       connID,
		isClosed:     false,
		MsgHandle:    msgHandle,
		ExitBuffChan: make(chan bool, 1),
		msgChan:      make(chan []byte), //初始化
		msgBufChan:   make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
		property:     make(map[string]interface{}), //对连接属性map初始化
	}

	//将新创建的Conn添加到连接管理中
	c.TcpServer.GetConnMgr().Add(c) //
	return c
}

/*
	写消息Goroutine，用户将数据发送给客户端
*/

func (c *Connection) StartWriter() {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn Writer exit!]")
	for {
		select {
		case data := <-c.msgChan:
			//有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send Data error:", err, "conn writer exit")
				return
			}

		case data, ok := <-c.msgBufChan:
			//针对有缓冲channel需要进行数据处理

			if ok {
				//有数据要写给客户端
				if _, err := c.Conn.Write(data); err != nil {
					fmt.Println("Send Buff Data error: ", err)
					return
				} else {
					fmt.Println("msgBuffChan is Closed")
					break
				}
			}
		case <-c.ExitBuffChan:
			//conn 已经关闭
			return
		}
	}
}

// StartReader 处理conn读数据的Goroutine
func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is running")
	defer fmt.Println(c.RemoteAddr().String(), " conn reader exit")
	defer c.Stop()

	for {
		//得到当前客户端请求的Request数据
		//创建拆包解包的对象
		dp := NewDataPack()
		//读取客户端的MsgHead
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head err ", err)
			c.ExitBuffChan <- true
			continue
		}

		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack err ", err)
			c.ExitBuffChan <- true
			continue
		}

		//根据dataLen 读取data，放到msg.Data中
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data err ", err)
				c.ExitBuffChan <- true
				continue
			}
		}

		msg.SetData(data)
		fmt.Printf("server receive ,id = %d , msg = %s\n", msg.GetMsgId(), msg.GetData())
		req := Request{
			conn: c,
			msg:  msg,
		}

		if utils.GlobalObject.WorkerPoolSize > 0 {
			//已经启动工作池机制，将消息交给Worker处理
			c.MsgHandle.SendMsgToTaskQueue(&req)
		} else {
			//从绑定好的消息 和 对应的处理方法中执行对应的Handle方法
			go c.MsgHandle.DoMsgHandler(&req)
		}
	}
}

// Start 启动连接，让当前连接开始工作
func (c *Connection) Start() {
	//开启用于从客户端读取数据流程的Goroutine
	go c.StartReader()
	//开启用于写回客户端数据流程的Goroutine
	go c.StartWriter()
	//按照用户传递进来的创建连接时需要处理的业务，执行钩子方法
	c.TcpServer.CallOnConnStart(c)

	for {
		select {
		case <-c.ExitBuffChan:
			//得到退出消息，不再阻塞
			return
		}

	}
}

// Stop 停止连接，结束当前连接状态
func (c *Connection) Stop() {
	//1、如果当前连接已关闭
	if c.isClosed {
		return
	}
	c.isClosed = true
	// Connetion Stop() 如果用户注册了该连接的关闭回调服务,则在此刻应该显示调用
	c.TcpServer.CallOnConnStop(c)
	//关闭Socket 连接
	c.Conn.Close()

	//通知从缓冲队列读书句的业务，该连接已经关闭
	c.ExitBuffChan <- true
	//将连接从连接管理器中删除
	c.TcpServer.GetConnMgr().Remove(c) //将conn 从ConnManager中删除

	//关闭该连接的全部通道
	close(c.ExitBuffChan)
	close(c.msgChan)
}
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// SendMsg 将Message数据发送给远程的TCP客户端
func (c *Connection) SendMsg(msgID uint32, data []byte) error {
	if c.isClosed {
		return errors.New("connection closed when send msg")
	}

	//将data封包，并且发送
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgID, data))
	if err != nil {
		fmt.Println("pack error msg id = ", msgID)
		return errors.New("pack error msg")
	}

	//写回客户端
	//将之前直接回写给conn.Write的方法改为发送给channel，供Write读取
	c.msgChan <- msg

	return nil
}

func (c *Connection) SendBuffMsg(msgID uint32, data []byte) error {
	if c.isClosed {
		return errors.New("connection closed when send msg")
	}

	//将data封包，并且发送
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgID, data))
	if err != nil {
		fmt.Println("pack error msg id = ", msgID)
		return errors.New("pack error msg")
	}

	//写回客户端
	//将之前直接回写给conn.Write的方法改为发送给channel，供Write读取
	c.msgBufChan <- msg

	return nil
}

//设置连接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	c.property[key] = value
}

//获取连接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}
}

//移除连接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	delete(c.property, key)
}
